import re
from rmasm.exceptions import *
from rmasm.word_syntax import *
from typing import *
from dataclasses import dataclass
import enum


@dataclass(order=True)
class Command:
    pass


@dataclass(order=True)
class MoveCommand(Command):
    left: Word
    right: Word


@dataclass(order=True)
class MacroCommand(Command):
    macro: Macro


@dataclass(order=True)
class JumpCommand(Command):
    target: str
    condition: Union[BinaryOperation, UnaryOperation, None]


@dataclass(order=True)
class EndCommand(JumpCommand):
    target: str = ''
    condition: Union[str, None] = None


@dataclass(order=True)
class TagCommand(Command):
    name: str


@dataclass(order=True)
class RawCommand(Command):
    command: str


_move_command_re = re.compile(r'([@#$\-\[\]\w]+) *([\+\-\*\/\%\^]?)= *(.+)', re.S)


def match_command(line: str) -> Union[Command, None]:
    line = line.strip()
    if not line:
        return None
    if line.startswith('>'):
        return RawCommand(line[1:].strip())
    if line.endswith(':'):
        return TagCommand(line[:-1].strip())
    if line == 'end':
        return EndCommand()
    if line.startswith('jump '):
        target_condition = line[5:].split(' if ')
        if len(target_condition) == 1:
            return JumpCommand(target_condition[0].strip(), condition=None)
        elif len(target_condition) == 2:
            condition_str = target_condition[1].strip()
            word = match_word(condition_str)
            if isinstance(word, BinaryOperation) or isinstance(word, UnaryOperation):
                return JumpCommand(target_condition[0].strip(), condition=word)
            else:
                raise ParseException(f"Jump 命令条件解析错误：{line}，条件应该是一个二元或一元表达式")
        else:
            raise ParseException(f"Jump 命令解析错误：{line}，格式应该是 [jump 目标标号 if 条件]")
    m = _move_command_re.match(line)
    if m:
        if m.group(2):
            return MoveCommand(left=match_word(m.group(1)), right=BinaryOperation(
                name=m.group(2),
                left=m.group(1),
                right=m.group(3)
            ))
        else:
            return MoveCommand(left=match_word(m.group(1)), right=match_word(m.group(3)))
    macro = match_marco(line)
    if macro:
        return MacroCommand(macro)
    return macro
